/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package meteocal.entityManager;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import meteocal.entity.Event;
import meteocal.entity.Invitation;
import meteocal.entity.Notification;
import meteocal.entity.Status;
import meteocal.entity.Users;

/**
 *
 * @author andrea
 */
@Stateless
public class EventManager {
    
    @PersistenceContext
    private EntityManager em;
    
    @Inject
    private UserManager um;
    
    /**
     * Saves in the database the event in param.
     * @param event
     */
    public void save(Event event){
        try{
            em.persist(event);
        }
        catch(EntityExistsException e){
            Logger.getLogger(EventManager.class.getName())
                    .severe("The entity already exists!");
        }
    }
    
    /**
     * Updates the event informations in the database.
     * @param event
     * @throws IllegalArgumentException if the event doesn't exist
     */
    public void update(Event event){
        if(!exist(event)){
            throw new IllegalArgumentException("The event doesn't exist");
        } else{
            em.merge(event);
        }
    }
    
    /**
     * Removes the event from the database.
     * @param event
     */
    public void delete(Event event){
        em.remove(em.merge(event));
    }
    
    /**
     * Retrieves all the events stored in the database.
     * @return the resulting list
     */
    public List<Event> getAllEvents(){
        Query query = em.createQuery("SELECT e FROM Event e");
        try{
            List<Event> events = query.getResultList();
            return events;
        }
        catch(NoResultException e){
            return null;
        }
    }
   
    /**
     * Retrieves all the public events stored in the database.
     * @return the resulting list
     */
    public List<Event> getAllPublicEvents(){
        Query query = em.createQuery("SELECT e FROM Event e "
                + "WHERE e.visible = TRUE");
        try{
            List<Event> events = query.getResultList();
            return events;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Retrieves all the invitations related to the event.
     * @param event
     * @return the resulting list
     */
    public List<Invitation> getInvitations(Event event){
        Query query = em.createQuery("SELECT i FROM Invitation i WHERE i.event = :event");
        try{
            List<Invitation> invitations = query.setParameter("event", event)
                    .getResultList();
            return invitations;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Retrieves all the notifications related to the event.
     * @param event
     * @return the resulting list
     */
    public List<Notification> getNotifications(Event event){
        Query query = em.createQuery("SELECT n FROM Notification n WHERE n.event = :event");
        try{
            List<Notification> notifications = query.setParameter("event", event)
                    .getResultList();
            return notifications;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Retrieves all the participants (with already accepted invitation) to the event.
     * @param event
     * @return the resulting list
     */
    public List<Users> getParticipants(Event event){
        Query query = em.createQuery("SELECT u FROM Users u, Invitation i "
                + "WHERE i.event = :event "
                + "AND i.user.email = u.email "
                + "AND i.status = :accepted");
        try{
            List<Users> users = query.setParameter("event", event)
                    .setParameter("accepted", Status.ACCEPTED)
                    .getResultList();
            return users;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Retrieves only the users that still have a pending invitation to the event.
     * @param event
     * @return the resulting list
     */
    public List<Users> getPending(Event event){
        Query query = em.createQuery("SELECT u FROM Users u, Invitation i "
                + "WHERE i.event = :event "
                + "AND i.user.email = u.email "
                + "AND i.status = :pending");
        try{
            List<Users> users = query.setParameter("event", event)
                    .setParameter("pending", Status.PENDING)
                    .getResultList();
            return users;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Retrieves all the users invited to the event, either they have already 
     * accepted or not.
     * @param event
     * @return the resulting list
     */
    public List<Users> getInvited(Event event){
        Query query = em.createQuery("SELECT u FROM Users u, Invitation i "
                + "WHERE i.event = :event "
                + "AND i.user.email = u.email "
                + "AND (i.status = :accepted OR i.status = :pending)");
        try{
            List<Users> users = query.setParameter("event", event)
                    .setParameter("accepted", Status.ACCEPTED)
                    .setParameter("pending", Status.PENDING)
                    .getResultList();
            return users;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Retrieves the emails of the users invited to the event, if they haven't 
     * already refused the invitation.
     * @param event
     * @return the resulting list
     */
    public List<String> getInvitedEmail(Event event){
        List<Users> users = getInvited(event);
        List<String> emails = new ArrayList<>();
        for(Users u : users){
            emails.add(u.getEmail());
        }
        return emails;
    }
    
    /**
     * Searches events with a given title.
     * @param title
     * @return the list of the resulting events (may be null)
     */
    public List<Event> findByTitle(String title){
        Query query = em.createQuery("SELECT e FROM Event e WHERE e.title = :title");
        try{
            List<Event> eventsList = (List<Event>) query.setParameter("title", title)
                    .setMaxResults(10)
                    .getResultList();
            return eventsList;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Searches events with the given keyword in their title
     * @param keyword
     * @param max number of results
     * @return the resulting list of events (may be null)
     */
    public List<Event> findByIncompleteTitle(String keyword,int max){
        Query checkInvited = em.createQuery("SELECT i FROM Invitation i");
        if(checkInvited.getResultList().isEmpty()){
            Query query = em.createQuery("SELECT DISTINCT e FROM Event e "
                    + "WHERE e.title LIKE :title "
                    + "AND (e.visible = TRUE "
                    + "OR e.creator = :user)");
            try{
                return query.setParameter("title", "%" +keyword+ "%")
                        .setParameter("user", um.getLoggedUser())
                        .setMaxResults(max)
                        .getResultList();
            }
            catch(NoResultException e){
                return null;
            }
        }
        Query query = em.createQuery("SELECT DISTINCT e FROM Event e, Invitation i "
                + "WHERE e.title LIKE :title "
                + "AND (e.visible = TRUE "
                + "OR e.creator = :user "
                + "OR (i.event.id = e.id AND i.user = :user))");
        try{
            return query.setParameter("title", "%" +keyword+ "%")
                    .setParameter("user", um.getLoggedUser())
                    .setMaxResults(max)
                    .getResultList();
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Searches events created by a given user.
     * @param user
     * @return the list of the resulting events (may be null)
     */
    public List<Event> findByCreator(Users user){
        Query query = em.createQuery("SELECT e FROM Event e WHERE e.creator = :creator");
        try{
            List<Event> eventsList = (List<Event>) query.setParameter("creator", user)
                    .getResultList();
            return eventsList;
        }
        catch(NoResultException e){
            return null;
        }
    }
    
    /**
     * Find the event by its primary key.
     * @param id
     * @return the event if exists or null if the id is null or if the event
     * doesn't exist
     */
    public Event findById(Long id){
        if(id == null)
            return null;
        return em.find(Event.class, id);
    }
    
    /**
     * Checks if an event exists in the database.
     * @param event
     * @return true if it exists, false otherwise
     */
    public boolean exist(Event event){
        return em.find(Event.class, event.getId()) != null;
    }
    
}
